package com.leyou.search.service.impl;

import com.leyou.client.ItemClient;
import com.leyou.common.exception.LyException;
import com.leyou.item.dto.BrandDTO;
import com.leyou.item.dto.CategoryDTO;
import com.leyou.search.client.GoodsRepository;
import com.leyou.search.dto.SearchParamDTO;
import com.leyou.search.entity.Goods;
import com.leyou.search.service.SearchService;
import com.leyou.starter.elastic.entity.PageInfo;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
import org.elasticsearch.search.aggregations.bucket.nested.Nested;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Mono;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author mySun
 * @create 2021-07-23 15:19
 */
@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private ItemClient itemClient;

    @Override
    public Mono<List<String>> getSuggestion(String key) {
        return goodsRepository.suggestBySingleField("suggestion",key);
    }


    @Override
    public Mono<PageInfo<Goods>> pageGoods(SearchParamDTO searchParamDTO) {
        SearchSourceBuilder sourceBuilder = getSearchSourceBuilder(searchParamDTO);


        sourceBuilder.from(searchParamDTO.getFrom());
        sourceBuilder.size(searchParamDTO.getSize());


        return goodsRepository.queryBySourceBuilderForPageHighlight(sourceBuilder);
    }

    @Override
    public Mono<Map<String, List<?>>> getFilterCondition(SearchParamDTO searchParamDTO) {

        SearchSourceBuilder searchSourceBuilder = getSearchSourceBuilder(searchParamDTO);
        //分桶名称
        String brandAgg = "brandAgg";
        String categoryAgg = "categoryAgg";
        String specsAgg = "specsAgg";
        String nameAgg = "nameAgg";
        String valueAgg = "valueAgg";

        //根据品牌id分桶
        searchSourceBuilder.aggregation(AggregationBuilders.terms(brandAgg).field("brandId").size(100));
        //根据分类id分桶
        searchSourceBuilder.aggregation(AggregationBuilders.terms(categoryAgg).field("categoryId").size(100));



        //根据规格分桶
        AggregationBuilder aggregationBuilder = AggregationBuilders.nested(specsAgg, "specs")
                //根据规格名称分桶
                .subAggregation(AggregationBuilders.terms(nameAgg).field("specs.name").size(100)
                        //根据规格值分桶
                        .subAggregation(AggregationBuilders.terms(valueAgg).field("specs.value").size(100)));

        searchSourceBuilder.aggregation(aggregationBuilder);

       return goodsRepository.aggregationBySourceBuilder(searchSourceBuilder).map(aggregations -> {
           //要返回的结果类型
            Map<String, List<?>> result = new LinkedHashMap<>();

            //获取categoryId分桶结果
           Terms categoryIdAgg = aggregations.get(categoryAgg);
           //根据分桶结果获取分类id的集合
           List<Long> categoryIds = categoryIdAgg.getBuckets().stream().map(categoryBucket -> categoryBucket.getKeyAsNumber().longValue()).collect(Collectors.toList());
           if(!CollectionUtils.isEmpty(categoryIds)){
               //rpc根据分类id集合获取分类集合
               List<CategoryDTO> categoryDTOList = itemClient.queryCategoryByIds(categoryIds);
               result.put("分类",categoryDTOList);
           }

           //获取brandId分桶结果
           Terms brandIdAgg = aggregations.get(brandAgg);
           //根据分桶结果获取品牌id的集合
           List<Long> brandIds = brandIdAgg.getBuckets().stream().map(brandBucket -> brandBucket.getKeyAsNumber().longValue()).collect(Collectors.toList());
           if(!CollectionUtils.isEmpty(brandIds)){
               //rpc根据品牌id集合获取品牌集合
               List<BrandDTO> brandDTOList = itemClient.queryBrandByBrandIds(brandIds);
               result.put("品牌",brandDTOList);
           }

           //获取规格分桶结果
           Nested specs = aggregations.get(specsAgg);
           Terms specNameAgg = specs.getAggregations().get(nameAgg);
           specNameAgg.getBuckets().forEach(specNameBucket -> {
               //获取规格名称
               String specName = specNameBucket.getKeyAsString();
               //获取子聚合 specValue
               Terms specValueAgg = specNameBucket.getAggregations().get(valueAgg);
               //获取规格对应的值的集合
               List<String> specValueList = specValueAgg.getBuckets().stream().map(MultiBucketsAggregation.Bucket::getKeyAsString).collect(Collectors.toList());
               result.put(specName,specValueList);
           });


           return result;
        });

    }


    private SearchSourceBuilder getSearchSourceBuilder(SearchParamDTO searchParamDTO) {
        if(searchParamDTO.getKey().isEmpty()){
            throw new LyException(400,"查询条件为空");
        }

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //bool查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("title", searchParamDTO.getKey()).operator(Operator.AND));

        Map<String, String> filters = searchParamDTO.getFilters();
        if(!CollectionUtils.isEmpty(filters)){
                filters.forEach((specName, specValue) -> {
                    if ("分类".equals(specName)) {
                        specName = "categoryId";
                        boolQueryBuilder.filter(QueryBuilders.termQuery(specName, specValue));

                    } else if ("品牌".equals(specName)) {
                        specName = "brandId";
                        boolQueryBuilder.filter(QueryBuilders.termQuery(specName, specValue));
                    } else {
                        BoolQueryBuilder filterBool = QueryBuilders.boolQuery();
                        filterBool.must(QueryBuilders.termQuery("specs.name", specName));
                        filterBool.must(QueryBuilders.termQuery("specs.value", specValue));

                        NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("specs", filterBool, ScoreMode.None);

                        boolQueryBuilder.filter(nestedQuery);
                    }

                });
        }



        sourceBuilder.query(boolQueryBuilder);
        return sourceBuilder;
    }
}
